#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <time.h>

#define MAX_EVENTS 10
#define MSGTYPE_R 11
#define MSGTYPE_L 22
#define MSGTYPE_Q 33
#define MSGTYPE_H 44

void serveClientRequest(int sockfd);
void client_registration(int sockfd, char *name, char *date);
void client_login(int sockfd, char *name, char *date);
void client_query(int sockfd, char *date, char *name);
int find_word(char *date, char *buf);
void record_client_history_query(int sockfd,char *data, char *name);
void client_send_history(int sockfd, char* name);
void get_local_time(char *p);

typedef struct {
    int type;       // 记录用户的操作状态
    char name[32];  // 用户名称
    char date[300]; // 数据部分
} MSG_TXT;

sqlite3 *db;

int main(int argc, const char **argv) {
    if (argc != 3) {
        printf("参数数目不正确\n请传入IP和端口号参数\n");
        exit(-1);
    }

    // 首先连接数据库
    char db_addr[] = "./Dictionary.db"; // 修正为字符数组
    if (sqlite3_open(db_addr, &db) == SQLITE_OK) {
        puts("数据库连接成功");
    } else {
        puts("数据库连接失败,请检查数据库文件是否存在");
        exit(-1);
    }

    // 创建套接字,准备连入网络的端口
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("套接字创建失败");
        exit(-1);
    }
    puts("套接字创建成功");

    // 准备服务器的IP地址和端口号
    struct sockaddr_in server_addr = {0};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);

    // 将套接字绑定到IP地址和端口号并校验
    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("绑定失败");
        exit(-1);
    }
    puts("绑定成功");

    printf("服务器的IP为%s, 端口号为%d\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));

    // 将套接字转换成被动套接字使其可以接收来自客户端的请求
    // 可以同时接收来自5个不同客户端的请求
    if (listen(sockfd, 5) == -1) {
        perror("套接字转换失败");
        exit(-1);
    }
    puts("被动套接字监听来自客户端的请求");

    // 创建 epoll 实例
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(-1);
    }

    // 将监听套接字添加到 epoll 实例中
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
        perror("epoll_ctl: listen_sock");
        exit(-1);
    }

    // 事件数组
    struct epoll_event events[MAX_EVENTS];

    while (1) {
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            exit(-1);
        }

        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == sockfd) {
                // 处理新连接
                struct sockaddr_in client_addr;
                socklen_t client_len = sizeof(client_addr);
                int new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
                if (new_sockfd == -1) {
                    perror("接收出错");
                    continue;
                }
                puts("新客户端连接");

                // 将新套接字添加到 epoll 实例中
                ev.events = EPOLLIN;
                ev.data.fd = new_sockfd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_sockfd, &ev) == -1) {
                    perror("epoll_ctl: conn_sock");
                    close(new_sockfd);
                }
            } else {
                // 处理客户端数据
                int client_sockfd = events[i].data.fd;
                serveClientRequest(client_sockfd);
            }
        }
    }

    close(sockfd);
    close(epoll_fd);
    sqlite3_close(db);
    return 0;
}

/// @brief 处理客户端请求
/// @param sockfd 套接字
/// @return
void serveClientRequest(int sockfd) {
    MSG_TXT msg_from_client = {0};
    int ret = recv(sockfd, &msg_from_client, sizeof(msg_from_client), 0);
    if (ret == 0) {
        printf("客户端断开连接\n");
        close(sockfd);
        return ;
    } else if (ret == -1) {
        perror("recv");
        close(sockfd);
        return ;
    }

    printf("客户端请求类型为%d\n", msg_from_client.type);

    switch (msg_from_client.type) {
        case MSGTYPE_R:
            client_registration(sockfd, msg_from_client.name, msg_from_client.date);
            break;
        case MSGTYPE_L:
            client_login(sockfd, msg_from_client.name, msg_from_client.date);
            break;
        case MSGTYPE_Q:
            client_query(sockfd, msg_from_client.date, msg_from_client.name);
            break;
        case MSGTYPE_H:
            client_send_history(sockfd, msg_from_client.name);
            break;
        default:
            break;
    }

    return ;
}

/// @brief  处理客户端注册请求
/// @param sockfd 套接字
/// @param name 用户名称
/// @param date 数据部分-密码
void client_registration(int sockfd, char *name, char *date) {
    printf("客户端请求注册名为%s的用户\n", name);

    // 去数据库中插入数据
    // 数据库中的用户名称会设置为主键唯一，是无法插入相同用户名称的信息的
    MSG_TXT msg_sendto_client = {0};
    msg_sendto_client.type = MSGTYPE_R;
    char *err_sqlite = NULL;
    char sql[256] = {0};
    sprintf(sql, "insert into user values('%s', '%s');", name, date);
    if (sqlite3_exec(db, sql, NULL, NULL, &err_sqlite) != SQLITE_OK) {
        printf("插入数据库失败\n");
        // 发送错误代码给客户端
        strcpy(msg_sendto_client.date, err_sqlite);
        if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
            perror("发送给客户端失败");
            return;
        }
        
        return;
    }

    printf("插入数据库成功\n");
    strcpy(msg_sendto_client.date, "注册成功");
    if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
        perror("发送成功代码给客户端失败");
        return;
    }
}

/// @brief 处理客户端登录请求
/// @param sockfd 套接字
/// @param name 用户名称
/// @param date 数据部分-密码
void client_login(int sockfd, char *name, char *date) {
    printf("客户端请求登录名为%s的用户\n", name);
    MSG_TXT msg_sendto_client = {0};
    msg_sendto_client.type = MSGTYPE_L;
    char *err_sqlite = NULL;
    char sql[256] = {0};
    char **res_select = NULL;
    int nrow = 0, ncolumn = 0;
    sprintf(sql, "select * from user where name = '%s' and passwd = '%s';", name, date);
    int ret = sqlite3_get_table(db, sql, &res_select, &nrow, &ncolumn, NULL);
    // -----------------------------------------------------------------------------------
    if (ret != SQLITE_OK) {
        printf("查询数据库失败\n");
        return;
    }

    if(nrow == 0){
        strcpy(msg_sendto_client.date, "登陆失败");
        if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
            perror("发送错误代码给客户端失败");
            return;
        }
    }

    strcpy(msg_sendto_client.date, "登陆成功");
    if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
        perror("发送成功代码给客户端失败");
        return;
    }
}

/// @brief 处理客户端查询请求
/// @param sockfd 套接字
/// @param date 
void client_query(int sockfd, char *date, char *name) {
    printf("客户端请求查询单词%s\n", date);

    // 使用buf记录可能查找到的单词解释
    char buf[256] = {0};
    if (find_word(date, buf)) {
        // 找到了单词，并且buf存放的就是该单词的解释
        record_client_history_query(sockfd, date, name);
        MSG_TXT msg_sendto_client = {0};
        msg_sendto_client.type = MSGTYPE_Q;
        strcpy(msg_sendto_client.name, name);
        strcpy(msg_sendto_client.date, buf);
        if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
            perror("发送成功代码给客户端失败");
            return;
        }
    } else {
        // 没找到单词
        MSG_TXT msg_sendto_client = {0};
        msg_sendto_client.type = MSGTYPE_Q;
        strcpy(msg_sendto_client.name, name);
        strcpy(msg_sendto_client.date, "单词不存在");
        if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
            perror("发送成功代码给客户端失败");
            return;
        }
    }
}

/// @brief 查找单词
/// @param date 需要查找的单词
/// @param buf 查询结果的记录
/// @return 查询结果
int find_word(char *date, char *buf) {
    FILE *fp = fopen("Dictionary.txt", "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return 0;
    }

    char line[259];
    int found = 0;

    while (fgets(line, sizeof(line), fp)) {
        line[strcspn(line, "\n")] = 0; // 去除换行符

        char *current = line;

        // 跳过空格或制表符
        while (*current == ' ' || *current == '\t') {
            current++;
        }

        // 提取单词
        char *word_start = current;
        while (*current != ' ' && *current != '\t' && *current != '\0') {
            current++;
        }

        // 分隔单词
        *current = '\0';
        current++;

        // 跳过单词后的空格或制表符
        while (*current == ' ' || *current == '\t') {
            current++;
        }

        // 此时current指向了单词后面的第一个内容
        // 检查是否匹配
        if (strcmp(word_start, date) == 0) {
            // 定义部分
            strcpy(buf, current);
            found = 1;
            break;
        }
    }

    fclose(fp);
    return found;
}

/// @brief 记录查询历史
/// @param sockfd 套接字
/// @param data 数据-单词
/// @param name 用户名
void record_client_history_query(int sockfd,char *data, char *name){
    char sql[200]={0};
    sprintf(sql, "create table if not exists %s(data text, date text);", name);

    char *errmsg;
    int ret=sqlite3_exec(db, sql, NULL, NULL, &errmsg);
    if(ret!=0)
    {
        printf("建表失败%s\n", errmsg);
        return ;
    }

    memset(sql, 0, sizeof(sql));
    char buffer[50]={0};
    get_local_time(buffer);
    printf("buffer=%s\ndata=%s\n",buffer, data);
    sprintf(sql, "insert into %s values('%s','%s')", name, data, buffer);
    ret=sqlite3_exec(db,sql,NULL,NULL,&errmsg);
    if(ret!=0)
    {
        printf("插入失败%s\n",errmsg);
    }
}

/// @brief 获取本地时间
/// @param p 时间字符串
void get_local_time(char *p){
    time_t raw_time;
    time(&raw_time);    //当前时间存储到raw_time中去
    struct tm* tp=localtime(&raw_time);
    if(tp==NULL)
    {
        perror("tp failed ");
        return;
    }

    sprintf(p,"%d-%d-%d", tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday);
}

void client_send_history(int sockfd, char* name){
    char sql[200]={0};
    sprintf(sql,"select *from %s",name);
    char **resultp=NULL;
    int row;
    int column;
    char *errmsg = NULL;
    int ret=sqlite3_get_table(db,sql,&resultp,&row,&column,&errmsg);
    if(ret!=0){
        printf("历史查找失败:%s\n",errmsg);
    }

    MSG_TXT msg_sendto_client={0};
    msg_sendto_client.type=MSGTYPE_H;
    strcpy(msg_sendto_client.name, name);
    int i=0;
    for(i=2;i<(row+1)*column; i += 2){
        // 数据打包
        sprintf(msg_sendto_client.date, "%s\t\t%s", resultp[i], resultp[i+1]);
        if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
            perror("发送给客户端失败");
            return;
        }
        memset(msg_sendto_client.date,0,sizeof(msg_sendto_client.date));
    }
    sprintf(msg_sendto_client.date, "无");
    if (send(sockfd, &msg_sendto_client, sizeof(msg_sendto_client), 0) == -1) {
        perror("发送给客户端失败");
        return;
    }
}